package pers.yurwisher.rubick.oauth2.exception;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.NestedRuntimeException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import pers.yurwisher.rubick.common.enums.tip.CustomTipEnum;
import pers.yurwisher.rubick.common.exception.CustomException;
import pers.yurwisher.rubick.common.wrapper.R;

import java.util.List;
import java.util.Optional;

/**
 * @author yq
 * @date 2021/04/15 22:07
 * @description oauth2异常回调
 * @since V1.0.0
 */
@Slf4j
@RestControllerAdvice(basePackages = "pers.yurwisher.rubick.oauth2.controller")
public class OAuth2ExceptionHandler  implements WebResponseExceptionTranslator<OAuth2Exception> {

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    /**
     * 全局异常
     */
    @ExceptionHandler(Throwable.class)
    public R<String> defaultErrorHandler(Throwable e) {
        log.error("全局异常回调", e);
        return R.fail(CustomTipEnum.FAIL);
    }

    /**
     * 请求参数缺失
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public R<String> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
        return R.fail(e.getParameterName() + "缺失");
    }

    /**
     * 全局参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<String> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        if (CollectionUtil.isNotEmpty(errors)) {
            //返回第一个错误的信息
            Optional<ObjectError> optional = errors.stream().filter(error -> StrUtil.isNotEmpty(error.getDefaultMessage())).findFirst();
            return optional.map(error -> R.fail(error.getDefaultMessage())).orElseGet(() -> R.fail(CustomTipEnum.FAIL));
        } else {
            log.error("自定义校验错误...", e);
            return R.fail(CustomTipEnum.FAIL);
        }
    }

    /**
     * spring 抛出的异常
     */
    @ExceptionHandler(NestedRuntimeException.class)
    public R<String> nestedRuntimeExceptionHandler(NestedRuntimeException e) {
        log.info("spring 异常", e);
        return R.fail(CustomTipEnum.FAIL);
    }

    /**
     * 405
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<String> httpRequestMethodNotSupportedExceptionHandle(HttpRequestMethodNotSupportedException e) {
        return R.fail(CustomTipEnum.METHOD_NOT_ALLOWED);
    }

    /**
     * 415
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public R<String> httpMediaTypeNotSupportedExceptionHandle(HttpMediaTypeNotSupportedException e) {
        return R.fail(CustomTipEnum.UNSUPPORTED_MEDIA_TYPE);
    }

    /**
     * 404
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public R<String> noHandlerFoundExceptionHandle(NoHandlerFoundException e) {
        return R.fail(CustomTipEnum.NOT_FOUND);
    }

    @ExceptionHandler(OAuth2Exception.class)
    public R<String> oauth2ExceptionHandle(OAuth2Exception e){
        return R.fail(e.getLocalizedMessage());
    }

    @ExceptionHandler(AuthenticationException.class)
    public R<String> authenticationExceptionHandle(AuthenticationException e){
        return R.fail(e.getLocalizedMessage());
    }

    /**
     * 自定义异常回调
     */
    @ExceptionHandler(CustomException.class)
    public R<String> customExceptionHandler(CustomException e) {
        return R.fail(e.getTip());
    }

    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
        log.info("异常回调: [{}]",e.getLocalizedMessage());
        // Try to extract a SpringSecurityException from the stacktrace
        Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
        OAuth2Exception ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain);
        return new ResponseEntity<>(ase, HttpStatus.OK);
    }
}
